package com.linkedin.dagli.placeholder;

import com.linkedin.dagli.placeholder.internal.PlaceholderInternalAPI;
import com.linkedin.dagli.producer.AbstractRootProducer;
import com.linkedin.dagli.producer.ProducerType;


/**
 * {@link Placeholder}s are the inputs that are provided when the DAG is executed (i.e. training,
 * inference, or both).  They, along with {@link com.linkedin.dagli.generator.Generator}s, are the root nodes in the
 * Dagli graph.  Every DAG will have at least one {@link Placeholder}.
 *
 * If we think of each example processed by the DAG as a "row", a {@link Placeholder} corresponds to a "column",
 * although the objects passed as inputs can of course be arbitrarily complex and it is usually better to pass larger
 * numbers of values within a well-defined container object (i.e. a @Struct) rather than through individual
 * {@link Placeholder} values.  This is because a @Struct makes it much harder to, e.g. accidentally provide values to
 * the graph in the wrong order and thus create logic bugs.
 *
 * {@link Placeholder}s can be subclassed to add convenience methods.  For example, derived {@link Placeholder}s are
 * automatically generated by @Structs to provide methods that pull values out of the @Struct being supplied by the
 * {@link Placeholder}, which is more convenient than explicitly instantiating a transformer for the same purpose.
 *
 * @param <R> the type of value supplied by this {@link Placeholder}
 */
public class Placeholder<R>
    extends AbstractRootProducer<R, PlaceholderInternalAPI<R, Placeholder<R>>, Placeholder<R>>
    implements ProducerType<R, Placeholder<R>> {
  private static final long serialVersionUID = 1;

  /**
   * Creates a new {@link Placeholder} with the specified name.  This will be the name returned by the getName() method;
   * if null is provided, a default name will be returned by {@link #getName()} instead.
   *
   * @param name a name to use for this {@link Placeholder}.
   */
  public Placeholder(String name) {
    super(name);
  }

  /**
   * Create a new {@link Placeholder}.
   */
  public Placeholder() {
    this(null);
  }

  @Override
  protected PlaceholderInternalAPI<R, Placeholder<R>> createInternalAPI() {
    return new InternalAPI();
  }

  protected class InternalAPI
      extends AbstractRootProducer<R, PlaceholderInternalAPI<R, Placeholder<R>>, Placeholder<R>>.InternalAPI
      implements PlaceholderInternalAPI<R, Placeholder<R>> { }

  // We use handle equality (equivalent to applying the @HandleEquality annotation), but we override the
  // computeEqualsUnsafe/computeHashCode methods so any subclasses will also use handle equality by default.
  // Subclasses may use a different method of determining equality, but this will make sense only in very limited
  // situations.
  @Override
  protected boolean computeEqualsUnsafe(Placeholder<R> other) {
    return super.handleEquality(other);
  }

  // override method so derived classes inherit (see computeEqualsUnsafe(...) above)
  @Override
  protected int computeHashCode() {
    return super.handleHashCode();
  }

  /**
   * Casts a producer to an effective "supertype" interface.  The semantics of the producer guarantee that the returned
   * type is valid for the instance.
   *
   * Note that although this and other {@code cast(...)} methods are safe, this safety extends only to the
   * interfaces for which they are implemented.  The covariance and contravariance relationships existing for these interfaces
   * do not necessarily hold for their derived classes.  For example, a {@code PreparedTransformer<String>} is also a
   * {@code PreparedTransformer<Object>}, but a {@code MyTransformer<String>} cannot necessarily be safely treated as a
   * {@code MyTransformer<Object>}.
   *
   * @param placeholder the placeholder to cast
   * @param <R> the type of result of the returned placeholder
   * @return the passed placeholder, typed to a new "supertype" of the original
   */
  @SuppressWarnings("unchecked")
  public static <R> Placeholder<R> cast(Placeholder<? extends R> placeholder) {
    return (Placeholder<R>) placeholder;
  }
}
